package Tools;

import MyClass.Memory;
import MyClass.Transaction;
import MyThread.*;
import org.omg.PortableInterceptor.INACTIVE;

import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.util.*;

/**
 * @Author: 0162977
 * @Date: 2020/11/12 15:22
 */
public class Dispatch {
    private static Memory[] publicBuf;
    private static List<Transaction> transactionList = new ArrayList<Transaction>();
    private static Map<Integer ,TransactionThread> threadMap = new HashMap<Integer, TransactionThread>();
    private static Map<Integer ,Long> startT = new HashMap<Integer ,Long>() ;
    private static Map<Integer ,Long> verificationT = new HashMap<Integer ,Long>() ;
    private static Map<Integer ,Long> finishT = new HashMap<Integer ,Long>() ;
    private static Map<Integer ,List<Integer>> waitThread = new HashMap<Integer ,List<Integer>>() ;
    static {
        Memory memory1 = new Memory();
        Memory memory2 = new Memory();
        memory1.setPubBufNo(0);
        memory2.setPubBufNo(1);
        memory1.setTimeFalg(0);
        memory2.setTimeFalg(1);
        try {
            memory1.setPubBufData(FileTools.FileInPut("./data/0.txt"));
            memory2.setPubBufData(FileTools.FileInPut("./data/1024.txt"));
        } catch (IOException e) {
            e.printStackTrace();
        }
        publicBuf = new Memory[]{memory1,memory2};
        System.out.println("===========内存准备就绪=============");
    }
    public static void pushDisk() throws IOException {
        FileTools.FileOutPut("./data/"+String.valueOf(publicBuf[1].getPubBufNo()*1024)+".txt",publicBuf[1].getPubBufData());
        FileTools.FileOutPut("./data/"+String.valueOf(publicBuf[0].getPubBufNo()*1024)+".txt",publicBuf[0].getPubBufData());
    }

    private static void changeMemory(int targetBlock ) throws IOException {
        int[][] arr = FileTools.FileInPut("./data/"+String.valueOf(targetBlock*1024)+".txt");
        Memory memory= new Memory();
        memory.setPubBufNo(targetBlock);
        memory.setPubBufData(arr);
        if (publicBuf[0].getTimeFalg() > publicBuf[1].getTimeFalg()){
            memory.setTimeFalg(publicBuf[0].getTimeFalg() +1);
            FileTools.FileOutPut("./data/"+String.valueOf(publicBuf[1].getPubBufNo()*1024)+".txt",publicBuf[1].getPubBufData());

            publicBuf[1] = memory;
        }else {
            memory.setTimeFalg(publicBuf[1].getTimeFalg() +1);
            FileTools.FileOutPut("./data/"+String.valueOf(publicBuf[0].getPubBufNo()*1024)+".txt",publicBuf[0].getPubBufData());

            publicBuf[0] = memory;
        }
    }

    private static void update(int index,int data) throws IOException {
        int memoryNO = index/1024;
        for(int i= 0 ; i<2 ;i++){
            if(memoryNO == publicBuf[i].getPubBufNo()){
                int arrNo = (index%1024)/4;
                int[][] arr = publicBuf[i].getPubBufData();
                arr[arrNo/16][arrNo%16] = data;
                publicBuf[i].setPubBufData(arr);
                return;
            }
        }
        changeMemory(memoryNO);
        update(index,data);
    }

    private static int getData(int index) throws IOException {
        int memoryNO = index/1024;
        for(int i= 0 ; i<2 ;i++){
            if(memoryNO == publicBuf[i].getPubBufNo()){
                int arrNo = (index%1024)/4;
                int[][] arr = publicBuf[i].getPubBufData();
                return arr[arrNo/16][arrNo%16];
            }
        }
        changeMemory(memoryNO);
        getData(index);
        return 0;
    }

    public static void addTransaction(int TranNo) throws IOException {
        List<String> content = new ArrayList<String>();
        BufferedReader in = new BufferedReader(new FileReader("T"+String.valueOf(TranNo)+".txt"));
        String line;
        while((line = in.readLine()) != null){
            content.add(line);
        }
        in.close();

        Transaction t = new Transaction();
        t.setContent(content);
        t.setTNo(TranNo);

        transactionList.add(t);
    }

    public static Transaction getTransactionByNo(int TNo){
        for (Transaction transaction : transactionList) {
            if (transaction.getTNo() == TNo) {
                return transaction;
            }
        }

        return null;
    }

    public static void openThread(int TNo){
        TransactionThread myThread = new TransactionThread(TNo);
        threadMap.put(TNo,myThread);
        Thread t = new Thread(myThread);
        t.start();

    }

    private static boolean inThisList(int key,Set<Integer> list){
        for (Integer kk : list){
            if (key == kk){
                return true;
            }
        }
        return false;
    }

    public static void deleteTranTime(int TNo){
        startT.remove(TNo);
        verificationT.remove(TNo);
        finishT.remove(TNo);
    }

    public static void addStartTime(int TNo,Long ts){
        updateTranRWSet(TNo);
        startT.put(TNo,ts);
    }

    public static int addVerificationTime(int TNo,Long ts){
        // 返回值说明 1表示验证成功，0表示验证失败 有冲突 回滚，2表示过早验证 等待
        for(int id = TNo-1;id>0 ;id--){
            if(!inThisList(id,verificationT.keySet())){
                List<Integer> waitList;
                if (inThisList(id,waitThread.keySet())){
                    waitList = waitThread.get(id);
                }else {
                    waitList = new ArrayList<Integer>();
                }
                waitList.add(TNo);
                waitThread.put(id,waitList);
                return 2;
            }
        }

        for(Integer key : verificationT.keySet()){
            if(!inThisList(key,finishT.keySet())){
                if(!Collections.disjoint(getTransactionByNo(key).getWriteSet(),getTransactionByNo(TNo).getReadSet())){
                    return 0;
                }
            }else if(finishT.get(key)> startT.get(TNo)){
                if(!Collections.disjoint(getTransactionByNo(key).getWriteSet(),getTransactionByNo(TNo).getReadSet())){
                    return 0;
                }
            }
            if(!inThisList(key,finishT.keySet())){
                if(!Collections.disjoint(getTransactionByNo(key).getWriteSet(),getTransactionByNo(TNo).getWriteSet())){
                    return 0;
                }
            }
        }
        verificationT.put(TNo,ts);
        return 1;
    }

    public static void addFinishTime(int TNo,Long ts){
        finishT.put(TNo,ts);
        if (waitThread.get(TNo)==null){
            return;
        }
        for (Integer id : waitThread.get(TNo)){
            threadMap.get(id).resume();
        }
    }

    public static int readMemory(int dataIndex) throws IOException {
        return getData(dataIndex);
    }

    public static void writeMemory(int dataIndex,int data) throws IOException {
        update(dataIndex,data);
    }

    public static void updateTranRWSet(int TNo){
        Transaction tt = null;
        int j = 0;
        for (j =0 ;j<transactionList.size();j++){
            if (transactionList.get(j).getTNo() == TNo){
                tt =  transactionList.get(j);
                break;
            }
        }
        List<String> content = tt.getContent();
        List<Integer> ws = new ArrayList<Integer>();
        List<Integer> rs = new ArrayList<Integer>();

        for (int i= 0 ;i<content.size();i++){
            String temp = content.get(i);
            String op = temp.split(",")[0];
            if(op.equals("0") ){
                rs.add(Integer.parseInt(temp.split(",")[1]));
            }else if(op.equals("1")){
                ws.add(Integer.parseInt(temp.split(",")[1]));
            }
        }
        tt.setWriteSet(ws);
        tt.setReadSet(rs);
        transactionList.set(j,tt);
    }
}
